package com.xxg.renrenorder.manager;

import com.alibaba.fastjson.JSON;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayUtil;
import com.xxg.renrencommon.exception.ErrorMessage;
import com.xxg.renrencommon.exception.OrderManagerException;
import com.xxg.renrenorder.util.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigDecimal;
import java.security.Security;
import java.util.HashMap;
import java.util.Map;

/**
 * 微信小程序服务接口
 *
 * @author xxg
 * @date 2020/9/28
 */
@Component
public class LpWeChatPayManager {
    private static final Logger logger = LoggerFactory.getLogger(LpWeChatPayManager.class);

    private final LpMyConfig myConfig;
    private WXPay wxpay;
    private final static String FAIL = "FAIL";

    public LpWeChatPayManager(LpMyConfig myConfig) {
        this.myConfig = myConfig;
        try {
            this.myConfig.readCert();
        } catch (Exception e) {
            logger.error(ErrorMessage.WECHATPAY_ERROR + "\n错误原因:" + e.getMessage(), e);
        }
        wxpay = null;
        try {
            wxpay = new WXPay(this.myConfig);
        } catch (Exception e) {
            logger.error(ErrorMessage.WECHATPAY_ERROR + "\n错误信息:" + e.getMessage(), e);
        }
    }

    /**
     * 微信小程序支付
     *
     * @param body     支付商品标题
     * @param orderId  订单ID
     * @param totalFee 支付金额
     * @param openid   openid 小程序支付时必填
     * @return 支付结果
     * @throws OrderManagerException 异常信息
     */
    public Map<String, String> tradePay(String body, String orderId, String totalFee, String openid) throws OrderManagerException {
        Map<String, String> data = new HashMap<>(16);
        data.put("body", body);
        data.put("out_trade_no", orderId);
        data.put("device_info", "");
        data.put("fee_type", "CNY");
        BigDecimal total = (new BigDecimal(totalFee).setScale(2, BigDecimal.ROUND_HALF_UP)).multiply(new BigDecimal("100"));
        data.put("total_fee", String.valueOf(total.intValue()));
        data.put("spbill_create_ip", myConfig.getIp());
        data.put("notify_url", myConfig.getNotifyUrl());
        data.put("trade_type", "JSAPI");
        data.put("product_id", "");
        data.put("openid", openid);
        Map<String, String> resp;
        try {
            resp = wxpay.unifiedOrder(data);
            resultCheck(resp, data);
            Map<String, String> stringMap = new HashMap<>(16);
            stringMap.put("timeStamp", String.valueOf(WXPayUtil.getCurrentTimestamp()));
            stringMap.put("appId", resp.get("appid"));
            stringMap.put("nonceStr", resp.get("nonce_str"));
            stringMap.put("package", "prepay_id=" + resp.get("prepay_id"));
            stringMap.put("signType", "MD5");
            String sign = WXPayUtil.generateSignature(stringMap, myConfig.getKey());
            stringMap.put("paySign", sign);
            resp = stringMap;
        } catch (OrderManagerException e) {
            throw new OrderManagerException(e.getMessage());
        } catch (Exception e) {
            logger.error(ErrorMessage.WECHATPAY_ERROR + "\n接口tradePay" + "\n参数:" + JSON.toJSONString(data)
                    + "\n错误信息:" + e.getMessage(), e);
            throw new OrderManagerException(e.getMessage());
        }
        return resp;
    }

    /**
     * 查询订单支付状态
     *
     * @param orderId 订单ID
     * @return 支付状态结果
     * @throws OrderManagerException 异常信息
     */
    public Map<String, String> queryOrder(String orderId) throws OrderManagerException {
        Map<String, String> data = new HashMap<>(16);
        data.put("out_trade_no", orderId);
        Map<String, String> resp;
        try {
            resp = wxpay.orderQuery(data);
        } catch (Exception e) {
            logger.error(ErrorMessage.WECHATPAY_ERROR + "\n接口orderQuery" + "\n参数:" + JSON.toJSONString(data)
                    + "\n错误信息:" + e.getMessage(), e);
            throw new OrderManagerException(e.getMessage());
        }
        return resultCheck(resp, data);
    }

    /**
     * 微信退款
     *
     * @param orderId      订单ID
     * @param afterSalesId 售后ID
     * @param totalFee     订单总金额
     * @param reFundFee    退款金额
     * @return 退款结果
     * @throws OrderManagerException 异常信息
     */
    public Map<String, String> reFund(String orderId, String afterSalesId, String totalFee, String reFundFee) throws OrderManagerException {
        Map<String, String> data = new HashMap<>(16);
        data.put("out_trade_no", orderId);
        data.put("out_refund_no", afterSalesId);
        data.put("refund_fee_type", "CNY");
        BigDecimal total = (new BigDecimal(totalFee).setScale(2, BigDecimal.ROUND_HALF_UP)).multiply(new BigDecimal("100"));
        data.put("total_fee", String.valueOf(total.intValue()));
        BigDecimal reFund = (new BigDecimal(reFundFee).setScale(2, BigDecimal.ROUND_HALF_UP)).multiply(new BigDecimal("100"));
        data.put("refund_fee", String.valueOf(reFund.intValue()));
        data.put("notify_url", myConfig.getReFundNotifyUrl());
        Map<String, String> resp;
        try {
            resp = wxpay.refund(data);
        } catch (Exception e) {
            logger.error(ErrorMessage.WECHATPAY_ERROR + "\n接口tradePay" + "\n参数:" + JSON.toJSONString(data)
                    + "\n错误信息:" + e.getMessage(), e);
            throw new OrderManagerException(e.getMessage());
        }
        return resultCheck(resp, data);
    }

    /**
     * 查询订单退款状态
     *
     * @param afterSalesId 售后订单ID
     * @return 退款状态
     * @throws OrderManagerException 异常信息
     */
    public Map<String, String> refundQuery(String afterSalesId) throws OrderManagerException {
        Map<String, String> data = new HashMap<>(16);
        data.put("out_refund_no", afterSalesId);
        Map<String, String> resp;
        try {
            resp = wxpay.refundQuery(data);
        } catch (Exception e) {
            logger.error(ErrorMessage.WECHATPAY_ERROR + "\n接口refundQuery" + "\n参数:" + JSON.toJSONString(data)
                    + "\n错误信息:" + e.getMessage(), e);
            throw new OrderManagerException(e.getMessage());
        }
        return resultCheck(resp, data);
    }

    /**
     * 微信接口结果检查
     *
     * @param resp 结果
     * @param data 参数
     * @return 返回正确的结果
     * @throws OrderManagerException 异常信息
     */
    private Map<String, String> resultCheck(Map<String, String> resp, Map<String, String> data) throws OrderManagerException {
        if (FAIL.equals(resp.get("return_code"))) {
            logger.error(ErrorMessage.WECHATPAY_ERROR + "\n接口tradePay" + "\n参数:" + JSON.toJSONString(data)
                    + "\n结果:" + JSON.toJSONString(resp) + "\n错误信息:" + resp.get("return_msg"));
            throw new OrderManagerException(resp.get("return_msg"));
        }
        if (FAIL.equals(resp.get("result_code"))) {
            logger.error(ErrorMessage.WECHATPAY_ERROR + "\n接口tradePay" + "\n参数:" + JSON.toJSONString(data)
                    + "\n结果:" + JSON.toJSONString(resp) + "\n错误信息:" + resp.get("err_code_des"));
            throw new OrderManagerException(resp.get("err_code_des"));
        }
        return resp;
    }

    /**
     * 微信支付通知消息处理（验签及结果解析）
     *
     * @param notifyStr 通知数据
     * @return 结果
     */
    public Map<String, String> payNotify(String notifyStr) {
        try {
            // 转换成map
            Map<String, String> resultMap = WXPayUtil.xmlToMap(notifyStr);
            // 验签
            if (wxpay.isPayResultNotifySignatureValid(resultMap)) {
                String returnCode = resultMap.get("return_code");
                if (FAIL.equals(returnCode)) {
                    String returnMsg = resultMap.get("return_msg");
                    logger.error(ErrorMessage.WECHATPAY_ERROR + "\n接口:payNotify" + "\n错误信息:" + returnMsg);
                    return null;
                }
                String resultCode = resultMap.get("result_code");
                if (FAIL.equals(resultCode)) {
                    String errCodeDes = resultMap.get("err_code_des");
                    logger.error(ErrorMessage.WECHATPAY_ERROR + "\n接口:payNotify" + "\n错误信息:" + errCodeDes);
                    return null;
                }
                return resultMap;
            } else {
                logger.error(ErrorMessage.WECHATPAY_ERROR + "\n接口:payNotify" + "\n错误信息:签名非法");
                return null;
            }
        } catch (Exception e) {
            logger.error(ErrorMessage.WECHATPAY_ERROR + "\n接口:payNotify" + "\n错误信息:" + e.getMessage(), e);
        }
        return null;
    }

    /**
     * 退款通知消息处理，解密
     *
     * @param notifyStr 退款通知消息
     * @return 结果
     */
    public Map<String, String> refundNotify(String notifyStr) {
        try {
            // 转换成map
            Map<String, String> resultMap = WXPayUtil.xmlToMap(notifyStr);
            String returnCode = resultMap.get("return_code");
            if (FAIL.equals(returnCode)) {
                String returnMsg = resultMap.get("return_msg");
                logger.error(ErrorMessage.WECHATPAY_ERROR + "\n接口:payNotify" + "\n错误信息:" + returnMsg);
                return null;
            }
            String reqInfo = resultMap.get("req_info");
            if (reqInfo != null) {
                byte[] bytes = Base64.decode(reqInfo);
                String key = DigestUtils.md5Hex(myConfig.getKey()).toLowerCase();
                if (Security.getProvider("BC") == null) {
                    Security.addProvider(new BouncyCastleProvider());
                }
                Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
                SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES");
                cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
                String req = new String(cipher.doFinal(bytes));
                return WXPayUtil.xmlToMap(req);
            } else {
                logger.error(ErrorMessage.WECHATPAY_ERROR + "\n接口:payNotify" + "\n错误信息:req_info为null");
                return null;
            }
        } catch (Exception e) {
            logger.error(ErrorMessage.WECHATPAY_ERROR + "\n接口:payNotify" + "\n错误信息:" + e.getMessage(), e);
        }
        return null;
    }
}
